package cz.uhk.max.beans;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.FacesException;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

import org.richfaces.component.html.HtmlTree;
import org.richfaces.event.NodeSelectedEvent;
import org.richfaces.model.TreeNode;
import org.richfaces.model.TreeNodeImpl;

import cz.uhk.max.exceptions.EquationException;
import cz.uhk.max.matrix.Matrix;
import cz.uhk.max.matrix.impl.EquationImpl;

public class BoundSolutionsBackingBean {
	private Matrix matrixA = new Matrix(new Integer(4), new Integer(3));
	private Matrix matrixB = new Matrix(new Integer(4), new Integer(3));

	private Matrix lowerBound = new Matrix(new Integer(3), new Integer(1));
	private Matrix upperBound = new Matrix(new Integer(3), new Integer(1));
	private List<Matrix> xMinWithBound = new ArrayList<Matrix>();
	private Matrix xminimumNonExist = new Matrix(new Integer(3), new Integer(1));
	private Matrix xmaximum = new Matrix(new Integer(3), new Integer(1));
	private Integer maxNumber = 999; // default maximum
	private TreeNode rootNode = null;
	private TreeNode<Matrix> rootNodeGeneric = null;
	private List<String> selectedNodeChildren = new ArrayList<String>();
	private String nodeTitle;

	public BoundSolutionsBackingBean() {
		xminimumNonExist.setCol(0, 0, -1);
		xminimumNonExist.setCol(1, 0, -1);
		xminimumNonExist.setCol(2, 0, -1);
	}

	private void addNodes(String path, TreeNode node,
			List<Matrix> xMinsWithBound) {
		int counter = 1;
		for (Iterator iterator = xMinWithBound.iterator(); iterator.hasNext();) {
			Matrix m = (Matrix) iterator.next();
			TreeNodeImpl nodeImpl = new TreeNodeImpl();
			nodeImpl.setData("(" + m.getCol(0, 0).getValue() + ", "
					+ m.getCol(1, 0).getValue() + ", "
					+ m.getCol(2, 0).getValue() + ")");
			node.addChild(new Integer(counter), nodeImpl);
			counter++;
		}

	}

	private void loadTree() {
		FacesContext facesContext = FacesContext.getCurrentInstance();
		ExternalContext externalContext = facesContext.getExternalContext();

		rootNode = new TreeNodeImpl();
		addNodes(null, rootNode, xMinWithBound);
	}

	public void processSelection(NodeSelectedEvent event) {
		HtmlTree tree = (HtmlTree) event.getComponent();
		nodeTitle = (String) tree.getRowData();
		selectedNodeChildren.clear();
		TreeNode currentNode = tree.getModelTreeNode(tree.getRowKey());
		if (currentNode.isLeaf()) {
			selectedNodeChildren.add((String) currentNode.getData());
		} else {
			Iterator<Map.Entry<Object, TreeNode>> it = currentNode
					.getChildren();
			while (it != null && it.hasNext()) {
				Map.Entry<Object, TreeNode> entry = it.next();
				selectedNodeChildren.add(entry.getValue().getData().toString());
			}
		}
	}

	public TreeNode getTreeNode() {
		if (rootNode == null) {
			loadTree();
		}
		return rootNode;
	}

	public String getNodeTitle() {
		return nodeTitle;
	}

	public void setNodeTitle(String nodeTitle) {
		this.nodeTitle = nodeTitle;
	}

	public Integer getMaxNumber() {
		return maxNumber;
	}

	public void setMaxNumber(Integer maxNumber) {
		this.maxNumber = maxNumber;
	}

	public void randomAB(ActionEvent e) {
		matrixA.randomNumberGenerator(maxNumber);
		matrixB.randomNumberGenerator(maxNumber);
		upperBound.randomNumberGenerator(maxNumber);
		cz.uhk.max.matrix.IEquation equation;
		try {
			equation = new EquationImpl(matrixA, matrixB, maxNumber.longValue());
			xmaximum = equation.solutionWithUpperBoundForOneRow(matrixA
					.getRows().get(0), matrixB.getRows().get(0), upperBound);
		} catch (EquationException e1) {
			e1.printStackTrace();
		}

	}

	public void findXWithUpperBound(ActionEvent e) {
		cz.uhk.max.matrix.IEquation equation;
		try {
			equation = new EquationImpl(matrixA, matrixB, maxNumber.longValue());
			xmaximum = equation.solutionWithUpperBoundForOneRow(matrixA
					.getRows().get(0), matrixB.getRows().get(0), upperBound);
		} catch (EquationException e1) {
			e1.printStackTrace();
		}
	}

	public List<String> getStyleClasses() {
		List<String> styleClasses = initStyleClassList();
		String viewId = FacesContext.getCurrentInstance().getViewRoot()
				.getViewId();
		if (viewId.equals("indexId")) {
			styleClasses.add(2, "neviditelny");
		}
		return styleClasses;
	}

	private List<String> initStyleClassList() {
		List<String> styleClasses = new ArrayList<String>();
		for (int i = 0; i < 5; i++) {
			styleClasses.add("viditelny");
		}
		return styleClasses;
	}

	public void findXWithLowerBound(ActionEvent e) {
		cz.uhk.max.matrix.IEquation equation;
		List<Matrix> sols = new ArrayList<Matrix>();
		try {
			equation = new EquationImpl(matrixA, matrixB, maxNumber.longValue());
			sols = equation.solutionsWithLowerBoundForOneRow(matrixA.getRows()
					.get(0), matrixB.getRows().get(0), lowerBound);
			if (sols == null) {
				xMinWithBound = new ArrayList<Matrix>();
				xMinWithBound.add(xminimumNonExist);
			} else {
				xMinWithBound = new ArrayList<Matrix>(sols);
			}

		} catch (EquationException e1) {
			e1.printStackTrace();
		}
	}

	public void randomABlower(ActionEvent e) {
		matrixA.randomNumberGenerator(maxNumber);
		matrixB.randomNumberGenerator(maxNumber);
		lowerBound.randomNumberGenerator(maxNumber - 10);
		cz.uhk.max.matrix.IEquation equation;
		List<Matrix> sols = new ArrayList<Matrix>();
		try {
			equation = new EquationImpl(matrixA, matrixB, maxNumber.longValue());
			sols = equation.solutionsWithLowerBoundForOneRow(matrixA.getRows()
					.get(0), matrixB.getRows().get(0), lowerBound);
			if (sols == null) {
				xMinWithBound = new ArrayList<Matrix>();
				xMinWithBound.add(xminimumNonExist);
			} else {
				xMinWithBound = new ArrayList<Matrix>(sols);
			}

		} catch (EquationException e1) {
			e1.printStackTrace();
		}
	}

	public Matrix getMatrixA() {
		return matrixA;
	}

	public void setMatrixA(Matrix a) {
		matrixA = a;
	}

	public Matrix getMatrixB() {
		return matrixB;
	}

	public void setMatrixB(Matrix b) {
		matrixB = b;
	}

	public void setLowerBound(Matrix lowerBound) {
		this.lowerBound = lowerBound;
	}

	public Matrix getLowerBound() {
		return lowerBound;
	}

	public void setUpperBound(Matrix upperBound) {
		this.upperBound = upperBound;
	}

	public Matrix getUpperBound() {
		return upperBound;
	}

	public void setXminWithBound(List<Matrix> xminWithBound) {
		this.xMinWithBound = xminWithBound;
	}

	public List<Matrix> getMinimum() {
		return new ArrayList<Matrix>(xMinWithBound);
	}

	public void setXmaximum(Matrix xmaximum) {
		this.xmaximum = xmaximum;
	}

	public Matrix getXmaximum() {
		return xmaximum;
	}

	public Matrix getXminimumNonExist() {
		return xminimumNonExist;
	}

	public void setXminimumNonExist(Matrix xminimumNonExist) {
		this.xminimumNonExist = xminimumNonExist;
	}

	public void setRootNodeGeneric(TreeNode<Matrix> rootNodeGeneric) {
		this.rootNodeGeneric = rootNodeGeneric;
	}

	public TreeNode<Matrix> getRootNodeGeneric() {
		return rootNodeGeneric;
	}
}
